package com.renfei.cameralibrary.encoder;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.util.Log;

import com.renfei.medialibrary.recorder.AudioParams;
import com.renfei.medialibrary.recorder.VideoParams;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;

public class MediaAudioEncoder extends MediaEncoder {
    private static final boolean DEBUG = false;    // TODO set false on release
    private static final String TAG = "MediaAudioEncoder";

    private AudioParams mAudioParams;

    private AudioThread mAudioThread = null;

    public static final int SAMPLES_PER_FRAME = 1024;    // AAC, bytes/frame/channel

    public MediaAudioEncoder(final MediaMuxerWrapper muxer, AudioParams audioParams, final MediaEncoderListener listener) {
        super(muxer, listener);
        this.mAudioParams = audioParams;
    }

    @Override
    protected void prepare() throws IOException {
        if (DEBUG) Log.e(TAG, "prepare:");
        mTrackIndex = -1;
        mMuxerStarted = mIsEOS = false;


        final MediaFormat audioFormat = MediaFormat.createAudioFormat(AudioParams.MIME_TYPE, mAudioParams.getSampleRate(),
                mAudioParams.getChannel());
        audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
        audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, mAudioParams.getBitRate());
//        audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, AudioParams.BUFFER_SIZE);

        /**
         * 下面这两个配置i，是为了适配三星Galxy 8手机
         */
        audioFormat.setInteger(MediaFormat.KEY_CHANNEL_MASK, mAudioParams.getChannel());
        audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
        if (DEBUG) Log.e(TAG, "format: " + audioFormat);



        mMediaCodec = MediaCodec.createEncoderByType(AudioParams.MIME_TYPE);
        mMediaCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

        mMediaCodec.start();
        if (DEBUG) Log.e(TAG, "prepare finishing");
        if (mListener != null) {
            try {
                mListener.onPrepared(this);
            } catch (final Exception e) {
                Log.e(TAG, "prepare:", e);
            }
        }
    }

    @Override
    protected void startRecording() {
        super.startRecording();
        if (mAudioThread == null) {
            mAudioThread = new AudioThread(new WeakReference<>(this));
            mAudioThread.start();
        }
    }

    @Override
    protected void release() {
        mAudioThread = null;
        super.release();
    }

    private static final int[] AUDIO_SOURCES = new int[]{
            MediaRecorder.AudioSource.MIC
            //   ,
            //   MediaRecorder.AudioSource.DEFAULT,
            //   MediaRecorder.AudioSource.CAMCORDER,
            //   MediaRecorder.AudioSource.VOICE_COMMUNICATION,
            //   MediaRecorder.AudioSource.VOICE_RECOGNITION,
    };

    /**
     * Thread to capture audio data from internal mic as uncompressed 16bit PCM data
     * and write them to the MediaCodec encoder
     */
    private class AudioThread extends Thread {
        private WeakReference<MediaAudioEncoder> weakReference;

        public AudioThread(WeakReference<MediaAudioEncoder> weakReference) {
            this.weakReference = weakReference;
        }

        @Override
        public void run() {
            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
            int cnt = 0;
            int minBufferSize = AudioRecord.getMinBufferSize(
                    weakReference.get().mAudioParams.getSampleRate(),
                    weakReference.get().mAudioParams.getChannel(),
                    weakReference.get().mAudioParams.getAudioFormat());
            try {


                AudioRecord audioRecord = null;
                try {
                    audioRecord = new AudioRecord(
                            MediaRecorder.AudioSource.MIC,
                            weakReference.get().mAudioParams.getSampleRate(),
                            weakReference.get().mAudioParams.getChannel(),
                            weakReference.get().mAudioParams.getAudioFormat(), minBufferSize);
                    if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED)
                        audioRecord = null;
                } catch (final Exception e) {
                    audioRecord = null;
                }

                if (audioRecord != null) {
                    try {
                        if (mIsCapturing) {
                            if (DEBUG) Log.e(TAG, "AudioThread:start audio recording");
                            final ByteBuffer buf = ByteBuffer.allocateDirect(SAMPLES_PER_FRAME);
                            int readBytes;
                            audioRecord.startRecording();
                            try {
                                for (; mIsCapturing && !mRequestStop && !mIsEOS; ) {
                                    // read audio data from internal mic
                                    buf.clear();
                                    readBytes = audioRecord.read(buf, SAMPLES_PER_FRAME);
                                    if (readBytes > 0) {
                                        // set audio data to encoder
                                        buf.position(readBytes);
                                        buf.flip();
                                        encode(buf, readBytes, getPTSUs());
                                        frameAvailableSoon();
                                        cnt++;
                                        Log.e(TAG, "音频帧数量:    " + cnt);
                                    }
                                }
                                frameAvailableSoon();
                            } finally {
                                audioRecord.stop();
                            }
                        }
                    } finally {
                        audioRecord.release();
                    }
                } else {
                    if (DEBUG) Log.e(TAG, "failed to initialize AudioRecord");
                }
            } catch (final Exception e) {
                if (DEBUG) Log.e(TAG, "AudioThread#run", e);
            }
//            if (cnt == 0) {
//                final ByteBuffer buf = ByteBuffer.allocateDirect(SAMPLES_PER_FRAME);
//                for (int i = 0; mIsCapturing && (i < 5); i++) {
//                    buf.position(SAMPLES_PER_FRAME);
//                    buf.flip();
//                    try {
//                        encode(buf, SAMPLES_PER_FRAME, getPTSUs());
//                        frameAvailableSoon();
//                    } catch (final Exception e) {
//                        break;
//                    }
//                    synchronized (this) {
//                        try {
//                            wait(50);
//                        } catch (final InterruptedException e) {
//                        }
//                    }
//                }
//            }
            if (DEBUG) Log.e(TAG, "AudioThread:finished");
        }
    }


}
